#include "RequestFifo.h"

#include <string.h>
#include <stdio.h>
#include <errno.h>

RequestFifo::RequestFifo(string fileName, LogFile* logFile) {
	this->name = fileName;
    this->isDestroyed = false;
	this->logFile = logFile;

	// Se crea el fifo.
	mkfifo(this->name.c_str(), S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);

	// Se inicializa el file descriptor en -1.
	this->fd = -1;
	this->dummyFd = -1;
}

string RequestFifo::convertToString(int value) {
	stringstream stream;
	stream << value;
	return stream.str();
}

bool RequestFifo::writeRequest(SharedRecord request) {
	if (this->isDestroyed) {
		this->logFile->log("Error: No se puede escribir un request porque la instancia de RequestFifo ya fue destruida.");
		throw string("Esta instancia de RequestFifo ya fue destruida.");
	}

	// Se abre el file descriptor para escritura.
	if (this->fd == -1) {
		this->logFile->log("Abriendo el fifo para escritura: " + this->name);
		this->fd = open(this->name.c_str(), O_WRONLY);
	}

	// Se escriben los datos en el fifo.
	this->logFile->log("Escribiendo request en el fifo: " + this->name + " (file: " + request.sharedFilePath + " - fifo: " + request.requestFifo + ")");
	int result = write(
			this->fd,
			(const void *)reinterpret_cast<char*>(&request),
			sizeof(SharedRecord));

	this->logFile->log("Se termino de escribir el request en el fifo: " + this->name + ((result == -1) ? " (con errores - " + string(strerror(errno)) + ")" : " (exitoso - " + RequestFifo::convertToString(result) + "/" + RequestFifo::convertToString(sizeof(SharedRecord)) + " bytes escritos)"));

	return result == sizeof(SharedRecord);
}

bool RequestFifo::readRequest(SharedRecord &request) {
	if (this->isDestroyed) {
		this->logFile->log("Error: No se puede leer un request porque la instancia de RequestFifo ya fue destruida.");
		throw string("Esta instancia de RequestFifo ya fue destruida.");
	}

	// Se abre el fifo para lectura.
	if (this->fd == -1) {
		this->logFile->log("Abriendo el fifo para lectura: " + this->name);
		this->fd = open(this->name.c_str(), O_RDONLY);
	}

	// Tambien se abre el fifo para escritura para lograr un bloque en los read.
	if ((this->dummyFd == -1) && (this->fd > 0)) {
		this->logFile->log("Abriendo el fifo para escritura dummy: " + this->name);
		this->dummyFd = open(this->name.c_str(), O_WRONLY);
	}

	// Se leen los datos del fifo.
	this->logFile->log("Leyendo request desde fifo: " + this->name);
	int result = read(
			this->fd,
			(void *)reinterpret_cast<char*>(&request),
			sizeof(SharedRecord));

	this->logFile->log("Se termino de leer un request desde el fifo: " + this->name + ((result == -1) ? " (con errores - " + string(strerror(errno)) + ")" : " (exitoso - " + RequestFifo::convertToString(result) + "/" + RequestFifo::convertToString(sizeof(SharedRecord)) + " bytes leidos - file: " + request.sharedFilePath + " - fifo: " + request.requestFifo + ")"));

	return result == sizeof(SharedRecord);
}

void RequestFifo::closeFDs() {
	this->logFile->log("Cerrando los file descriptors del fifo: " + this->name);

    if (this->dummyFd != -1) {
        close(this->dummyFd);
    }

    if (this->fd != -1) {
        close(this->fd);
    }
}

void RequestFifo::destroy() {
	if (!this->isDestroyed) {
		this->closeFDs();

		this->logFile->log("Haciendo unlink del fifo: " + this->name);
		unlink(this->name.c_str());

		this->isDestroyed = true;
	}
}

RequestFifo::~RequestFifo() {
	if (!this->isDestroyed) {
		this->closeFDs();
	}
}
